Reentrantlock源码分析

ReentrantLock基于AQS实现的一种独占锁,也是重入锁。它实现了非公平锁和公平锁。本篇我们将通过源码对此做分析。

继承关系和构造

1
2
3
4
5
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
private final Sync sync;
……
}

ReentrantLock结构非常简单,它实现了Lock和Serializable接口,Lock接口包含了实现锁的基本方法,比如lock,unlock等。同时,ReentrantLock包含一个Sync对象,这里的Sync实际上为AQS的一种实现模型,关于AQS我们之前有所介绍,它是一种并发同步框架,ReentrantLock的两个模式的锁都是基于Sync实现的。

Sync同步模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;

abstract void lock();

/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
……
}

Sync实现了继承自AQS框架模型,它为子类提供了lock抽象方法。同时默认实现了nonfairTryAcquire即非公平锁的实现,以及tryRelease方法即锁的释放。ReentrantLock基于Sync实现了非公平和公平锁,默认是非公共锁。

1
2
3
public ReentrantLock() {
sync = new NonfairSync();//默认实现了非公平锁
}

###非公平锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;

/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}

protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}

基于Sync,NonfairSync实现了lock抽线方法,并且同时实现AQS的tryAcquire方法,这里我们看看非公平锁的实现。
非公平锁获取锁的实现:

  1. 通过CAS尝试获取锁,如果成功,当前线程将取得执行权
  2. 否则通过AQS的acquire来获取锁,最终通过tryAquire来获取锁,这里通过Sync的nonfairTryAcquire方法实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

在nonfairTryAcquire方法中,通过getState获取锁状态,如果为0,则表示当前锁处于空闲状态,尝试通过CAS获取锁,如果成功,则获取执行权。否则判断当前线程是否已经执行,如果已经执行的话就表示线程已经获取到了锁,需要重入,这里对锁状态加1,否则就返回false交给AQS处理,即将当前线程封装为Node节点添加到FIFO队列中,然后park线程。

公平锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;

final void lock() {
acquire(1);
}

/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}

公平锁的lock实现是通过其tryAcquire方法实现,它的实现原理如下:

  1. 首先获取获取锁的状态,如果为0表示锁空闲,则再判断是否队列中还有等待的前继线程,如果没有的话尝试通过CAS获取锁,如果成功,线程获取执行权,否则返回false,将线程添加到等待队列。否则判断当前线程是否已经执行,如果是,则表示重入,需要对锁状态+1。

区别

公平锁和非公平锁的区别在于:

  1. 非公平锁再lock时不管当前锁的状态,直接尝试通过CAS获取锁,而非公共锁在lock时首先判断当前锁状态,如果已经被占有,就直接去队列中等待了。

  2. 在非公平锁和公平锁中如果都检测到锁空闲,非公平锁直接也是通过CAS尝试获取锁,而公平锁会去检查等待队列是否有线程在等待,如果没有才尝试通过CAS获取锁,否则直接添加到等待队列中。

可见非公平锁和公平锁的最大区别都在于,非公平锁的实现常常显的粗暴,有点类似于插队,即它不管当前队列和锁的情况先直接去插队(尝试获取锁),成功了就直接去执行了,失败了就再到队列中排队,而公平锁显的温和许多,先看看队列或者锁是否都在忙,如果是就直接去队列中排队了。

坚持原创技术分享,您的支持将鼓励我继续创作!